/**
 *
 */
package neptune.web.client.view;

import neptune.web.client.WebInterface;
import neptune.web.client.commands.OpenClusterViewCommand;
import neptune.web.client.controller.PMNodeCommandHandler;
import neptune.web.client.model.cluster.SerializablePhysicalNode;
import neptune.web.client.services.ApplicationGenericService;
import neptune.web.client.services.ClusterService;
import neptune.web.client.services.PMHostedNodesDataProvider;
import neptune.web.client.view.components.NodesManager;
import neptune.web.client.view.components.NodesManager.NodeLiveDataProvider;
import neptune.web.client.view.components.dialogs.AddPhysicalMachineDialog;
import neptune.web.client.view.components.dialogs.EditPhysicalMachineDialog;

import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ButtonBase;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DisclosureEvent;
import com.google.gwt.user.client.ui.DisclosureHandler;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
 * @author Luigi Lorenzo Arpino [luigiarpino@yahoo.it]
 */
public class ClusterView extends View{

        public static final String DEFAULT_TOKEN = "Cluster";

        public static final String RUNNING_STATE = "RUNNING";
        public static final String BLOCKED_STATE = "BLOCKED";
        public static final String PAUSED_STATE = "PAUSED";
        public static final String SHUTDOWN_STATE = "SHUTDOWN";
        public static final String CRASHED_STATE = "CRASHED";
        public static final String DYING_STATE = "DYING";

        public static final int START_BUTTON = 0;
        public static final int PAUSE_BUTTON = 1;
        public static final int REBOOT_BUTTON = 2;
        public static final int CONSOLE_BUTTON = 3;

        public static final String BLANK_IMG = "img/blank.png";
        public static final String LOADING_IMG = "img/loading.gif";
        public static final String START_IMG = "img/start.png";
        public static final String PAUSE_IMG = "img/pause.png";
        public static final String REBOOT_IMG = "img/reboot.png";
        public static final String CONSOLE_IMG = "img/term.png";
        public static final String START_DIS_IMG = "img/start-dis.png";;
        public static final String PAUSE_DIS_IMG = "img/pause-dis.png";;
        public static final String REBOOT_DIS_IMG = "img/reboot-dis.png";;
        public static final String CONSOLE_DIS_IMG = "img/term-dis.png";
        public static final String NOT_IN_SYNC_IMG = "img/disconnected.png";
        public static final String OK_IMG = "img/ok.png";
        public static final String ERROR_IMG = "img/error.png";
        public static final String PERFORMANCE_IMG = "img/performance.png";
        public static final String PMADD_IMG = "img/pm_add.png";
        public static final String PMADDHOVER_IMG = "img/pm_add_hover.png";

        public static final int UPDATING_ICON = 2;
        public static final int NOT_IN_SYNC_ICON = 1;
        public static final int OK_ICON = 0;
        public static final int ERROR_ICON = 3;

        private FlexTable pmTable;

        private ClusterLiveDataProvider dataProvider;

        private ClickListener addPmListener = new ClickListener() {
                public void onClick(Widget sender) {
                        AddPhysicalMachineDialog dialog = new AddPhysicalMachineDialog();
                        dialog.center();
                }
        };

        /**
         * Questa interfaccia viene implementata dalla classe che realizza le
         * funzioni di aggiornamento dei dati presentati in questa vista.
         *
         * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
         *
         */
        public interface ClusterLiveDataProvider {

                /**
                 * Metodo chiamato quando e' richiesto l'aggiornamento dei dati per la
                 * macchina fisica il cui id e' passato come argomento.
                 *
                 * @param pmId
                 * @param view
                 */
                public void updatePMdata(String pmId, ClusterView view);

                /**
                 * Metodo chiamato quando e' richiesto l'aggiornamento di tutti i dati
                 * sulle physical machine della vista.
                 *
                 * @param view
                 */
                public void updatePMsData(ClusterView view);
        }

        public class PhysicalNodePanel extends Composite implements ClickListener {

                private FlexTable dataTable;
                private NodesManager nodesManager;

                private String id;
                private String nome;
                private String descrizione;
                private String ipAddress;
                private String cpu;
                private String ram;
                private String hd;

                /**
                 *
                 */
                public PhysicalNodePanel(String id, String nome, String descrizione,
                                String ipAddress, String CPU, String RAM, String HD) {
                        this.id = id;
                        this.nome = nome;
                        this.descrizione = descrizione;
                        this.ipAddress = ipAddress;
                        this.cpu = CPU;
                        this.ram = RAM;
                        this.hd = HD;
                        VerticalPanel vPanel = new VerticalPanel();
                        vPanel.setSize("100%", "100%");

                        dataTable = new FlexTable();
                        dataTable.setWidth("100%");
                        Image img = new Image(OK_IMG);
                        img.setTitle(descrizione);
                        dataTable.setWidget(0, 0, img);
                        dataTable.setText(0, 1, nome);
                        dataTable.setText(0, 2, ipAddress);
                        dataTable.setText(0, 3, CPU);
                        dataTable.setText(0, 4, RAM);
                        dataTable.setText(0, 5, HD);
                        Button butEdit = new Button("Edit");
                        butEdit.setTitle("Edit the configuration of " + nome
                                        + " physical machine ID=\"" + id + "\"");
                        butEdit.addClickListener(this);
                        dataTable.setWidget(0, 6, butEdit);

                        Button butRemove = new Button("Remove");
                        butRemove.setTitle("Remove " + nome
                                        + " physical machine from the cluster ID=\"" + id + "\"");
                        butRemove.addClickListener(this);
                        dataTable.setWidget(0, 7, butRemove);

                        PushButton butConsole = new PushButton(new Image(CONSOLE_IMG),
                                        new Image(CONSOLE_IMG));
                        butConsole.setTitle("Open SSH console to " + nome
                                        + " physical machine at \"" + ipAddress + "\"");
                        butConsole.addClickListener(this);
                        dataTable.setWidget(0, 8, butConsole);

                        PushButton butPerformance = new PushButton(new Image(
                                        PERFORMANCE_IMG), new Image(PERFORMANCE_IMG));
                        butPerformance.setTitle("View performance of " + nome
                                        + " physical machine with ID= \"" + id + "\"");
                        butPerformance.addClickListener(this);
                        dataTable.setWidget(0, 9, butPerformance);

                        dataTable.getColumnFormatter().setWidth(0, "16px");
                        dataTable.getColumnFormatter().setWidth(1, "16%");
                        dataTable.getColumnFormatter().setWidth(2, "16%");
                        dataTable.getColumnFormatter().setWidth(3, "16%");
                        dataTable.getColumnFormatter().setWidth(4, "16%");
                        dataTable.getColumnFormatter().setWidth(5, "16%");
                        dataTable.getColumnFormatter().setWidth(6, "5%");
                        dataTable.getColumnFormatter().setWidth(7, "5%");
                        dataTable.getColumnFormatter().setWidth(8, "5%");
                        dataTable.getColumnFormatter().setWidth(9, "5%");

                        vPanel.add(dataTable);
                        DisclosurePanel virtualMachinePanel = new DisclosurePanel(
                                        "Click to open/close hosted nodes list");
                        virtualMachinePanel.setWidth("100%");
                        virtualMachinePanel.setAnimationEnabled(true);
                        vPanel.add(virtualMachinePanel);

                        nodesManager = new NodesManager(150);
                        virtualMachinePanel.add(getNodesManager());
                        initWidget(vPanel);


                        // Imposta l'aggiornamento automatico dei nodi hostati da una pm
                        // quando viene aperta la lista
                        virtualMachinePanel.addEventHandler(new DisclosureHandler() {

                                public void onOpen(DisclosureEvent event) {
                                        if (nodesManager.getDataProvider() != null) {
                                                nodesManager.refreshData();
                                        }
                                }

                                public void onClose(DisclosureEvent event) {
                                }
                        });

                        // Styles
                        vPanel.setStylePrimaryName("ClusterView-NodePanel");
                        dataTable.setStylePrimaryName("ClusterView-NodePanel-dataTable");
                }

                /**
                 * @return the nodesManager
                 */
                public NodesManager getNodesManager() {
                        return nodesManager;
                }

                public String getId() {
                        return this.id;
                }

                public String getNome() {
                        return this.nome;
                }

                public void setNome(String nome) {
                        this.nome = nome;
                }

                public String getDescrizione() {
                        return this.descrizione;
                }

                public void setDescrizione(String descrizione) {
                        this.descrizione = descrizione;
                }

                public void setIcon(Image icon) {
                        dataTable.setWidget(0, 0, icon);
                }

                public String getIp() {
                        return this.ipAddress;
                }

                public void setIp(String ip) {
                        this.ipAddress = ip;
                        dataTable.setText(0, 2, ip);
                }

                public String getCPU() {
                        return this.cpu;
                }

                public void setCPU(String CPU) {
                        this.cpu = CPU;
                        dataTable.setText(0, 3, CPU);
                }

                public String getRAM() {
                        return this.ram;
                }

                public void setRAM(String ram) {
                        this.ram = ram;
                        dataTable.setText(0, 4, ram);
                }

                public String getHD() {
                        return this.hd;
                }

                public void setHD(String hd) {
                        this.hd = hd;
                        dataTable.setText(0, 5, hd);
                }

                public void onClick(Widget sender) {
                        // Leggo l'id della macchina fisica
                        String title = ((ButtonBase) sender).getTitle();
                        String tagInfo = title.split("\"")[1];

                        if (((ButtonBase) sender).getText().equals("Edit")) {
                                ClusterService.Util.getInstance().getPhysicalNode(tagInfo,
                                                new AsyncCallback<SerializablePhysicalNode>() {
                                                        public void onFailure(Throwable caught) {
                                                                String message = new String(
                                                                                "Error Unable to read iformation about physical machine: "
                                                                                                + caught.getMessage());
                                                                WebInterface.addMessageToConsoleAndShowDialog(
                                                                                message, true);
                                                        }

                                                        public void onSuccess(
                                                                        SerializablePhysicalNode sPhysicalNode) {
                                                                EditPhysicalMachineDialog dialog = new EditPhysicalMachineDialog(
                                                                                sPhysicalNode);
                                                                dialog.center();
                                                        }
                                                });
                        } else if (((ButtonBase) sender).getText().equals("Remove")) {

                                boolean isConfirmed = Window
                                                .confirm("Do you want to delete physical machine from the cluster?");
                                if (isConfirmed) {
                                        ClusterService.Util.getInstance().removePhysicalMachine(
                                                        tagInfo, new AsyncCallback<String>() {
                                                                public void onFailure(Throwable caught) {
                                                                        String message = new String(
                                                                                        "Error Unable to remove physical machine from the cluster: "
                                                                                                        + caught.getMessage());
                                                                        WebInterface
                                                                                        .addMessageToConsoleAndShowDialog(
                                                                                                        message, true);
                                                                }

                                                                public void onSuccess(String result) {
                                                                        String message = new String(
                                                                                        "The physical machine  has been removed from the cluster");
                                                                        WebInterface
                                                                                        .addMessageToConsoleAndShowDialog(
                                                                                                        message, false);
                                                                        OpenClusterViewCommand ocw = new OpenClusterViewCommand();
                                                                        ocw.execute();
                                                                }
                                                        });
                                }
                        } else if (((ButtonBase) sender).getTitle().contains("SSH")) {
                                ApplicationGenericService.Util.getInstance()
                                                .openRandomSSHRedirectPort(tagInfo,
                                                                new AsyncCallback<String>() {
                                                                        public void onFailure(Throwable caught) {
                                                                                String message = new String(
                                                                                                "Error Unable to open SSH port on web server: "
                                                                                                                + caught.getMessage());
                                                                                WebInterface
                                                                                                .addMessageToConsoleAndShowDialog(
                                                                                                                message, true);
                                                                        }

                                                                        public void onSuccess(String porta) {
                                                                                /*
                                                                                 * String host=Location.getHostName();
                                                                                 * //String message=new
                                                                                 * String("Port Open "
                                                                                 * +porta+" webserver="+host);
                                                                                 * //WebInterface
                                                                                 * .addMessageToConsoleAndShowDialog
                                                                                 * (message,false); ConsoleSSHDialog
                                                                                 * dialog=new ConsoleSSHDialog(host,
                                                                                 * porta); dialog.center();
                                                                                 */
                                                                                Window
                                                                                                .open("jcterm.jnlp", "_blank",
                                                                                                                "");
                                                                                // Window.open("sshapplet.html",
                                                                                // "_blank","");
                                                                                // Window.open("sshapplet.html",
                                                                                // "_blank","width=200,height=200,status=0,toolbar=0,menubar=0,location=0,resizable=0");
                                                                        }
                                                                });
                        } else if (((ButtonBase) sender).getTitle().contains("performance")) {
                                SuperView operative = (SuperView) ViewManager.getViewManager()
                                                .getViewByName(OperativeView.DEFAULT_TOKEN);
                                /*
                                 * if (operative.getViewByName(PerformanceView.DEFAULT_TOKEN) ==
                                 * null) { HistoryTokenUtils
                                 * .unregisterViewToken(PerformanceView.DEFAULT_TOKEN);
                                 * PerformanceView performanceView = new PerformanceView();
                                 * //clusterView.setDataProvider(new ClusterDataProvider());
                                 * operative.addView(performanceView); }
                                 */
                                PerformanceView performanceView = (PerformanceView) operative
                                                .getViewByName(PerformanceView.DEFAULT_TOKEN);
                                performanceView.setIdPhysicalMachine(tagInfo);
                                performanceView.startPollingToServer();
                                History.newItem(HistoryTokenUtils.buildToken(
                                                OperativeView.DEFAULT_TOKEN,
                                                PerformanceView.DEFAULT_TOKEN));
                        }
                }

        }

        /**
         *
         */
        public ClusterView() {
                // Setting view params
                try {
                        this.setName(DEFAULT_TOKEN);
                } catch (DuplicatedTokenException e) {
                        ConsoleManager.addConsoleDebugMessage(e.getMessage(), true);
                }


                // Costruzione struttura interfaccia
                VerticalPanel vPanel = new VerticalPanel();
                vPanel.setSize("90%", "100%");

                // Header tabella PM
                FlexTable pmTableHeader = new FlexTable();
                pmTableHeader.setWidth("98%");
                pmTableHeader.setWidget(0, 0, new Image(BLANK_IMG));
                pmTableHeader.setText(0, 1, "Name");
                pmTableHeader.setText(0, 2, "IP Address");
                pmTableHeader.setText(0, 3, "# CPU");
                pmTableHeader.setText(0, 4, "RAM (MBs)");
                pmTableHeader.setText(0, 5, "HD (GBs)");
                pmTableHeader.setText(0, 6, "");
                pmTableHeader.setText(0, 7, "");
                pmTableHeader.setText(0, 8, "");

                pmTableHeader.getColumnFormatter().setWidth(0, "16px");
                pmTableHeader.getColumnFormatter().setWidth(1, "17%");
                pmTableHeader.getColumnFormatter().setWidth(2, "17%");
                pmTableHeader.getColumnFormatter().setWidth(3, "17%");
                pmTableHeader.getColumnFormatter().setWidth(4, "17%");
                pmTableHeader.getColumnFormatter().setWidth(5, "17%");
                pmTableHeader.getColumnFormatter().setWidth(6, "5%");
                pmTableHeader.getColumnFormatter().setWidth(7, "5%");
                pmTableHeader.getColumnFormatter().setWidth(8, "5%");

                vPanel.add(pmTableHeader);

                pmTable = new FlexTable();
                pmTable.setWidth("98%");
                ScrollPanel pmTableWrap = new ScrollPanel(pmTable);
                pmTableWrap.setSize("100%", "400px");
                vPanel.add(pmTableWrap);

                HorizontalPanel horizontalPanel = new HorizontalPanel();

                PushButton bt = new PushButton(new Image(PMADD_IMG), new Image(
                                PMADDHOVER_IMG));
                bt.addClickListener(addPmListener);
                bt.setTitle("Add a new Physical Machine to the cluster");
                horizontalPanel.add(bt);

                VerticalPanel mainPanel = new VerticalPanel();
                mainPanel.add(horizontalPanel);
                mainPanel.add(vPanel);

                initWidget(mainPanel);

                // Styles
                vPanel.setStylePrimaryName("ClusterView");
                pmTableHeader.setStylePrimaryName("ClusterView-pmTable");
                pmTableHeader.addStyleDependentName("header");
                pmTableWrap.setStylePrimaryName("ClusterView-pmTableWrap");
                pmTable.setStylePrimaryName("ClusterView-pmTable");
                bt.setStylePrimaryName("ClusterView-button");
                horizontalPanel.setStylePrimaryName("ClusterView-buttonPanel");
        }

        /**
         * Aggiunge una physical machine alla lista e restituisce il
         * PhysicalNodePanel che la reappresenta appena creato. In questo modo e'
         * possibile modificare alcune caratteristiche di quest'ultimo, per
         * questioni di presentazione.
         *
         * @param id
         * @param ipAddress
         * @param CPU
         * @param RAM
         * @param HD
         * @return
         */
        public PhysicalNodePanel addPhysicalMachine(String id, String nome,
                        String descrizione, String ipAddress, String CPU, String RAM,
                        String HD) {
                int insertIndex = this.pmTable.getRowCount();
                PhysicalNodePanel pmPanel = new PhysicalNodePanel(id, nome,
                                descrizione, ipAddress, CPU, RAM, HD);
                // Imposta data provider e command handler del nodes manager associato a
                // questa PM
                pmPanel.getNodesManager().setDataProvider(
                                new PMHostedNodesDataProvider(id));
                pmPanel.getNodesManager().addNodeCommandHandler(
                                new PMNodeCommandHandler(id));
                this.pmTable.setWidget(insertIndex, 0, pmPanel);
                return pmPanel;
        }

        public void setPhysicalMachineInfos(String id, String nome,
                        String descrizione, String ipAddress, String CPU, String RAM,
                        String HD) {
                for (int i = 0; i < pmTable.getRowCount(); i++) {
                        PhysicalNodePanel actualPanel = (PhysicalNodePanel) pmTable
                                        .getWidget(i, 0);

                        if (actualPanel.getId().equals(id)) {
                                actualPanel.setNome(nome);
                                actualPanel.setDescrizione(descrizione);
                                actualPanel.setIp(ipAddress);
                                actualPanel.setCPU(CPU);
                                actualPanel.setRAM(RAM);
                                actualPanel.setHD(HD);
                        }
                }
        }

        public void setPhysicalMachineInfos(String id, String CPU, String RAM,
                        String HD) {
                // TODO aggiungere le informazioni circa il nome e la descrizione della
                // macchina fisica
                for (int i = 0; i < pmTable.getRowCount(); i++) {
                        PhysicalNodePanel actualPanel = (PhysicalNodePanel) pmTable
                                        .getWidget(i, 0);
                        if (actualPanel.getId().equals(id)) {
                                actualPanel.setCPU(CPU);
                                actualPanel.setRAM(RAM);
                                actualPanel.setHD(HD);
                        }
                }
        }

        /**
         * Imposta l'icona vicino al nome del nodo fisico
         *
         * @param nodeId
         */
        public void setPMIcon(String nodeId, int icon) {
                for (int i = 0; i < pmTable.getRowCount(); i++) {
                        PhysicalNodePanel actualPanel = (PhysicalNodePanel) pmTable
                                        .getWidget(i, 0);

                        if (actualPanel.getId().equals(nodeId)) {
                                switch (icon) {
                                case UPDATING_ICON:
                                        actualPanel.setIcon(new Image(LOADING_IMG));
                                        break;
                                case NOT_IN_SYNC_ICON:
                                        actualPanel.setIcon(new Image(NOT_IN_SYNC_IMG));
                                        break;
                                case OK_ICON:
                                        actualPanel.setIcon(new Image(OK_IMG));
                                        break;
                                case ERROR_ICON:
                                        actualPanel.setIcon(new Image(ERROR_IMG));
                                        break;
                                }
                        }
                }
        }

        /**
         * Rimuove tutte le physical machines dalla lista
         *
         */
        public void removeAllPMs() {
                int rowCount = pmTable.getRowCount();
                for (int i = 0; i < rowCount; i++) {
                        pmTable.removeRow(0);
                }
        }

        /**
         * @param dataProvider
         *            the dataProvider to set
         */
        public void setDataProvider(ClusterLiveDataProvider dataProvider) {
                this.dataProvider = dataProvider;
        }

        /**
         * @return the dataProvider
         */
        public ClusterLiveDataProvider getDataProvider() {
                return dataProvider;
        }

        /**
         * Ricarica i dati tramite il {@link NodeLiveDataProvider} per il dato nodo
         *
         * @param nodeId
         */
        public void refreshSinglePMData(String pmId) {
                setPMIcon(pmId, UPDATING_ICON);
                dataProvider.updatePMdata(pmId, this);
                // L'aggiornamento del nodo prevede che siano aggiornati anche i dati
                // sulle macchine da lui hostate.
                for (int i = 0; i < pmTable.getRowCount(); i++) {
                        PhysicalNodePanel actualPanel = (PhysicalNodePanel) pmTable
                                        .getWidget(i, 0);

                        if (actualPanel.getId().equals(pmId)) {
                                actualPanel.getNodesManager().refreshData();
                        }
                }
        }

        /**
         * Ricarica i dati tramite il {@link ClusterLiveDataProvider}
         */
        public void refreshData() {
                removeAllPMs();
                if (dataProvider != null)
                        dataProvider.updatePMsData(this);
                else {
                        ConsoleManager.addConsoleDebugMessage(
                                        "ClusterView error: no Data Provider defined", true);
                }
        }
}
